package com.swapper.json.experimental.struct;

import com.swapper.json.JsonValue;
import com.swapper.json.JsonValueList;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

public final class JSON_ARRAY implements JsonValueList {
  private static final int DEFAULT_CAPACITY = 10;
  private static final JsonValue[] EMPTY_VALUES = {};
  private static final JsonValue[] DEFAULT_CAPACITY_EMPTY_VALUES = {};
  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

  private int size;
  private transient JsonValue[] array;
  private transient int modCount;

  public JSON_ARRAY() {
    array = DEFAULT_CAPACITY_EMPTY_VALUES;
  }

  public JSON_ARRAY(int initialCapacity) {
    if (initialCapacity > 0) {
      array = new JsonValue[initialCapacity];
    } else if (initialCapacity == 0) {
      array = EMPTY_VALUES;
    } else {
      throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
  }

  @Override
  public int size() {
    return size;
  }

  @Override
  public boolean isEmpty() {
    return size == 0;
  }

  private void growCapacity(int minCapacity) {
    array = Arrays.copyOf(array, newCapacity(minCapacity));
  }

  private int newCapacity(int minCapacity) {
    int oldCapacity = array.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity <= minCapacity) {
      if (array == DEFAULT_CAPACITY_EMPTY_VALUES) {
        return Math.max(minCapacity, DEFAULT_CAPACITY);
      }
      if (minCapacity >= 0) {
        return minCapacity;
      }
      throw new OutOfMemoryError();
    }
    if (newCapacity > MAX_ARRAY_SIZE && minCapacity >= 0) {
      return minCapacity > MAX_ARRAY_SIZE ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }
    throw new OutOfMemoryError();
  }

  private static void checkIndex(int index, int size) {
    if (index >= size || index < 0) {
      throw new IndexOutOfBoundsException(String.format("Index: %d, Size: %d.", index, size));
    }
  }

  public int indexOf(JsonValue value) {
    JsonValue[] arr = array;
    for (int i = 0, s = size; i < s; ++i) {
      if (value.equals(arr[i])) {
        return i;
      }
    }
    return -1;
  }

  @Override
  public int lastIndexOf(JsonValue value) {
    JsonValue[] arr = array;
    for (int i = size - 1; i >= 0; --i) {
      if (value.equals(arr[i])) {
        return i;
      }
    }
    return -1;
  }

  @Override
  public JsonValue get(int index) {
    checkIndex(index, size);
    return array[index];
  }

  @Override
  public JsonValue set(int index, JsonValue value) {
    checkIndex(index, size);
    JsonValue oldValue = array[index];
    array[index] = value;
    return oldValue;
  }

  @Override
  public void append(JsonValue value) {
    ++modCount;
    int s;
    JsonValue[] arr;
    if ((s = size) == (arr = array).length) {
      growCapacity(s + 1);
    }
    arr[s] = value;
    size = s + 1;
  }

  @Override
  public boolean appends(Collection<? extends JsonValue> values) {
    return false;
  }

  @Override
  public void insert(int index, JsonValue value) {
    checkIndex(index, size + 1);
    ++modCount;
    int s;
    JsonValue[] arr;
    if ((s = size) == (arr = array).length) {
      growCapacity(s + 1);
    }
    if (index < s) {
      System.arraycopy(arr, index, arr, index + 1, s - index);
    }
    arr[index] = value;
    this.size = s + 1;
  }

  @Override
  public boolean inserts(int index, Collection<? extends JsonValue> values) {
    return false;
  }

  @Override
  public JsonValue remove(int index) {
    checkIndex(index, size + 1);
    ++modCount;
    int newSize;
    JsonValue[] arr = array;
    JsonValue oldValue = arr[index];
    if ((newSize = size - 1) > index) {
      System.arraycopy(arr, index + 1, arr, index, newSize - index);
    }
    arr[size = newSize] = null;
    return oldValue;
  }

  @Override
  public boolean remove(JsonValue value) {
    int index = indexOf(value);
    remove(index);
    return index != -1;
  }

  @Override
  public void clear() {
    ++modCount;
    JsonValue[] arr = array;
    for (int s = size, i = size = 0; i < s; ++i) {
      arr[i] = null;
    }
  }

  @Override
  public JsonValue[] toArray() {
    return Arrays.copyOf(array, size);
  }

  @Override
  public Iterator<JsonValue> iterator() {
    return null;
  }
}
